#include "INodo.h"


INodo::INodo(){ }

INodo::~INodo(){
	// Borra todo el arreglo
	std::list<IRegistro*>::iterator it;
	for (it = this->lRegistros.begin(); it != this->lRegistros.end(); ++it){
		delete (*it);
	}
}


Resultado INodo::agregarRegistro(IRegistro* reg){
	// Busca donde hay que insertar el registro
	std::list<IRegistro*>::iterator it;
	Resultado resul = buscarPosicion(reg, it);

	// Se chequea si la clave ya existe
	if (resul == CLAVE_DUPLICADA) return CLAVE_DUPLICADA;

	// Inserta el registro donde corresponde
	this->lRegistros.insert(it, reg);

	// Indica si hay sobreflujo, subflujo o esta todo bien
	resul = getEstado();

	// Para que se detecten los cambios producidos por las inserciones
	if (resul == OK)
		return MODIFICADO;
	else
		return resul;
}


// Devuelve un iterador donde hay que realizar la insercion del registro
Resultado INodo::buscarPosicion(IRegistro* reg, std::list<IRegistro*>::iterator &it){

	// Recorre toda la lista
	for (it = this->lRegistros.begin(); it != this->lRegistros.end(); ++it){

		// Busca un registro con id mayor al que quiero insertar
		if ( (*it)->getClave() > reg->getClave()){
			return OK;

		} else if (reg->getClave() == (*it)->getClave()){
			return CLAVE_DUPLICADA;
		}
	}

	// No se encontró ningun registro mayor
	// Entonces "reg" es el mayor del nodo y hay que insertarlo al final
	it = this->lRegistros.end();
	return OK;
}


Resultado INodo::quitarRegistroPorId(const IRegistro& reg){
	std::list<IRegistro*>::iterator it;
	for (it = this->lRegistros.begin(); it != this->lRegistros.end(); ++it){

		if (reg.getClave() == (*it)->getClave()){
			// Elimina el registro encontrado
			delete *it;
			this->lRegistros.erase(it);
			return OK;
		}
	}

	return NO_ENCONTRADO;
}


// Reemplaza el registro en el nodo por otro con el mismo id, si no existe uno devuelve error
Resultado INodo::modificarRegistroPorId(IRegistro* reg){
	std::list<IRegistro*>::iterator it;
	for (it = this->lRegistros.begin(); it != this->lRegistros.end(); ++it){

		// Encontrado, hay que reemplazarlo
		if ((*it)->getClave() == reg->getClave()){

			// Elimina el elemento anterior
			delete *it;
			it = this->lRegistros.erase(it);

			// Agrega el nuevo en el lugar del viejo
			this->lRegistros.insert(it, reg);
			return OK;
		}
	}

	return NO_ENCONTRADO;
}


const IRegistro& INodo::buscarRegistroPorId(const IRegistro& reg, bool busquedaExacta) const{
	std::list<IRegistro*>::const_iterator it;
	for (it = this->lRegistros.begin(); it != this->lRegistros.end(); ++it){

		// Para que esto funcione bien las clases derivadas de IRegistro
		// deben tener implementado un constructor copia desde la base al derivado
		if (busquedaExacta){
			if ( reg.getClave() <= (*it)->getClave() ) return **it;

		} else {
			if ( reg.getClave() < (*it)->getClave() ) return **it;
		}
	}

	// TODO: Saltar al proximo nodo
	// No se encontró ninguno mayor o igual al buscado
	return reg;
}


// Separa un nodo que esta casi lleno en dos medio vacios, "nodo" debe estar vacio
IRegistro& INodo::splitNodo(INodo& nodo){

	// Realiza la division por tamaños
	int cantQuedan = dividirRegistros();

	// Un iterador que apunta al primer elemento a mover
	std::list<IRegistro*>::iterator itBegin = this->lRegistros.begin();
	advance(itBegin, cantQuedan);

	/* "this->lRegistros" ya deberia estar ordenado y "nodo" deberia estar vacio
	 * por lo que esta operación deberia ser valida.
	 */

	// Mueve al inicio de "nodo" todos los elementos desde "itBegin" hasta el final
	nodo.lRegistros.splice(nodo.lRegistros.begin(),
			this->lRegistros, itBegin, this->lRegistros.end());

	// Devuelve un registro que contiene la clave que funciona de separador entre los nodos
	IRegistro& regSeparador = construirRegistroDivisor(nodo);

	return regSeparador;
}


// Devuelve la cant de registros que deben quedar en el nodo al hacer el split
int INodo::dividirRegistros() const{
	size_t tam = 0;
	int cantRegs = 0;
	std::list<IRegistro*>::const_iterator cit;
	for (cit = this->lRegistros.begin(); cit != this->lRegistros.end(); ++cit){

		// Cuenta los registros y va acumulando sus tamaños
		cantRegs++;
		tam += (*cit)->getSize();

		// Si se supera la cantidad minima que debe tener el nodo esa es la
		// cantidad de registros que deben quedar
		if (tam >= this->getTamBloque()/2)
			return cantRegs;
	}

	// Si se llegó a este punto hay menos registros que los necesarios para un split
	throw "Error: Se intentó un split con unsuficientes registros";
}


IRegistro& INodo::mergeNodo(INodo& nodo){
	throw "No será implementado";
}


// Devuelve el tamaño en bytes que ocupa en disco el nodo/bloque
Resultado INodo::getEstado() const{
	size_t tamTotal = getSize();

	// Chequea el estado del espacio en el nodo
	if (tamTotal > getTamBloque()){
		return OVERFLOW;

	} else if (tamTotal < getTamBloque()/2){
		return UNDERFLOW;

	} else {
		return OK;
	}
}


size_t INodo::getBytes() const{
	// Suma los tamaños de los registros
	size_t tamTotal = 0;
	std::list<IRegistro*>::const_iterator cit;
	for (cit = this->lRegistros.begin(); cit != this->lRegistros.end(); ++cit){
		tamTotal += (*cit)->getSize();
	}

	return tamTotal;
}


void INodo::clear(){
	// Primero borra todo el contenido del list
	std::list<IRegistro*>::iterator it;
	for (it = this->lRegistros.begin(); it != this->lRegistros.end(); ++it){
		delete (*it);
	}

	// Luego limpia todo el list
	this->lRegistros.clear();
}
